package com.hyper_yang.javase.supplement;

import java.util.HashMap;

/**
 * 说明: 这是一个 javaSE 学习的补充
 * @Author Yang
 * @Email hyper_y@163.com
 */

public class Illustrate {
/*
JVM内存--运行时数据区(Runtime Data Area)
    空间划分: 方法区、PC程序计数器、本地方法栈、堆、虚拟机栈
        方法区 (元空间): 存储的类的信息 包名 类名 静态信息 常量信息
        虚拟机栈 (栈空间): 方法运行时产生的局部信息，每当有一个方法执行时，在栈中为方法开辟一块空间，栈帧。
        堆 : 对象、数组
        程序计数器 PC : 存储下一步要执行的代码指令、地址，流程跳转的下一步指令
        本地方法栈 : java某些功能要靠其他语言实现，这是其他语言执行代码时所占的空间。
 */

/*
右键运行类， JVM通过类加载器，把类加到方法区里，方法区里寄存了一些 package信息等，只有 main方法可以执行，
当 main方法执行时，会占用栈空间，分配栈帧，里面的局部变量也在此处(eg: int a;int[] arr),但是数组赋的值不是基本数据类型，
这个时候分配地址占用堆空间(看数组内容大小)，数组变量 arr = 0x11 (地址值)
    基本数据类型的局部变量存储的就是在栈空间
    引用数据类型的局部变量存储的是堆空间的地址值
 */

/*
面向过程 POP: 关注【过程】，以【函数】为组织单位，是一种【执行者思维】，执行能力强、执行效率高。
面向对象 OOP: 关注【类】，以【类】为组织单位，是一种【设计者思维】，拓展能力强，可维护性高。
面向对象思想 特性: 封装【隐藏对象的内部实现细节，只对外提供公共访问方式】
                继承【继承父类的属性和方法，实现代码重用和扩展】
                多态【允许同一个接口或父类引用指向不同的子类对象，并调用子类的实现方法】

类的定义: 在计算机程序设计过程中，参照现实中的事物，将事物的属性特征、行为特征抽象出来，用类表示。
类的组成: 属性、行为/功能
类的内存存储: 第一次使用，进行类对象创建时，类装载器会将类的信息加载到方法区[对象.class 包括属性、方法]，这个类有地址 (eg: 0x11)
对象的内存存储: 基于方法区类，在堆空间创建对象(对象头指向方法区地址: 0x11),并分配默认值，也有个地址(eg: 0x22)。
    使用时: car.brand 改的信息就是 堆空间里的信息
           car.method() 执行时，在栈中为方法开辟一块空间，执行后出栈。 【执行方法在栈开辟，但堆中也存在实例方法】
对象的创建: new 对象() eg: main() --> Car car = 0x22;
       第二次 new同一个类的不同实例对象时[new 对象2()]，堆空间中新开辟空间 (eg: 0x33),对象头还是指向 0x11,但 Car car2 = 0x33;(是堆中的新对象地址)
对象的使用: 对象.属性  对象.方法
    ps: 栈中的内存，在方法执行结束后，出栈 (回收空间)、
        堆中的内存，当没有对象变量引用时，会变为“垃圾对象”等待 GC回收
        方法区的内存，当类在内存中没有其对象，也会等待 GC回收
匿名对象: 创建对象后，地址不赋给局部变量，一般适用于临时调用方法。 eg: new Dog().dark();
成员变量和局部变量的区别:
    1、编写位置不同:
        实例/成员变量: 类中，方法外
        局部变量: 类中，方法内
    2、内存位置不同:
        实例/成员变量: 堆，在对象中
        局部变量: 栈，在栈帧中
    3、作用范围不同:
        实例/成员变量: 类中任何位置
        局部变量: 声明所在的方法内
    4、默认值不同:
        实例/成员变量: 有默认值 0/null
        局部变量: 无默认值，必须赋值才可以使用
    5、生命周期不同:
        实例/成员变量: 对象创建时创建，对象销毁时销毁。
        局部变量: 方法执行时创建，方法执行后销毁。

Java依赖访问控制修饰符来控制类或成员的可见性范围，从而实现数据封装。
    public: 类内  包内  其他包子类   其他包非子类  均可访问
    protected: 类内   包内   其他包子类  均可访问
    default: 类内   包内   可访问
    private: 类内  可访问

this关键字:
    使用场景: 在实例方法中，当局部变量和实例变量重名时，在方法内部使用的是局部变量【局部变量优先】
    public void setName(String name){
         name = name;  // 局部变量 -> 局部变量   类属性并没有赋值(使用场景提到的局部变量优先)
         this.name = name; // 局部变量 -> 实例/成员变量   这样才能赋上值
    }
 */

/*
继承中的方法重写规则:
    1、子类重写父类方法时: 访问权限修饰符 子类 >= 父类
    2、子类重写父类方法时:
        返回值为基本类型，则子类重写必须一致；
        返回值时引用数据类型时，可以是父类返回值的子类类型。
    3、子类重写父类方法时: 方法名必须一致。
    4、子类重写父类方法时: 形参列表必须一致。

super() 的使用规则
    super、this 不能在构造器中并存
    this() 调用本类的其他构造器
    super() 调用父类的构造器
    最终子类必须有一个构造器的第一行是 super()

接口有同名方法问题: (接口可以有 属性、抽象方法、默认方法、静态方法、私有方法)
    接口有【同名抽象方法】时，实现类只需要实现一个
    接口有【同名默认方法】时，实现类必须重写这个方法，但可以 【接口名.super.method()】 调用 【只适用于默认方法，且默认方法是重复的】
接口与父类重名方法问题:
    继承父类优先，重写则无差别
接口与父类有重名属性时:  (接口属性的默认修饰符 public static final)
    ambiguous，需明确指定调用的是哪一个 super.name or B.name
 */

/*
接口与抽象类的区别
    相同点:
        都服务于子类，都可以被类继承或实现
        都不能 new对象、创建对象，都可以声明引用（变量）
        都可以包含抽象方法，子类必须重写抽象方法，否则子类也得是抽象类
    不同点:
        抽象类: 一般作为父类使用，可以写成员变量，抽象类有构造方法，实例方法。
              可以继承，但只能单继承，多重继承。
        接口: 一般作为功能或者标准的附加，可以写公开静态常量、静态方法、默认方法、私有方法，一般接口只写方法。
              可以实现，且可以是多实现，可以多继承。
 */

/*
多态的概述:
    前提: 必须要有继承关系或实现关系
        必须要有方法重写 （否则堕胎没有意义）
        父类类型的引用 指向 子类类型的对象
    问题: 以父类的角度看待子类对象，只能看到父类声明的内容，无法看到子类特有的。
    应用场景: 父类作为形参，子类做实参，参数的类型更加灵活
            父类作为数组的类型，元素存储子类对象，元素的类型更灵活。
            父类作为返回值类型，返回的子类对象，返回的类型更灵活。
多态的转型:
    向上转型: 子类对象存储在父类类型变量中
    向下转型: 将父类类型的变量，强转为子类类型
        可能出现 ClassCaseException 强制转换异常 --> 解决方案  instanceof   对象名 instanceof 类名

  eg: Animal a = new Dog(); // 【向上转型】这对后面的程序不一定可见
      if (a instanceof Cat) { // a不能确定是猫还是狗，也就不能直接调用子类方法，需要强转，为避免出现强制类型转换异常，强转之前判断。
           Cat cat = (Cat) a;
           cat.climbTree();
      }else if (a instanceof Dog) {
           Dog dog = (Dog) a;
           dog.lookHome();
      }else ...
新特性:  jdk 16
    上面的代码可简写为:
        if (a instanceof Cat cat) { // a不能确定是猫还是狗，也就不能直接调用子类方法，需要强转，为避免出现强制类型转换异常，强转之前判断。
               cat.climbTree();
          }else if (a instanceof Dog dog) {
               dog.lookHome();
          }else ...

 */

/*
内部类: 在类的内部，在定义一个完整的类
    普通成员内部类:
        1、外部类只能用public或缺省，而内部类可以用四个访问权限修饰符 修饰
        2、内部类可以使用外部类的资源 (属性和方法)
        3、外部类想要使用内部类的资源
            非静态资源: 内部类对象名.资源
            静态资源: 内部类类名.资源
        4、在外界创建内部类对象
            依赖于外部对象
            外部类类名.内部类类名 对象名 = new 外部类名().new 内部类名();
        5、普通成员内部类，也会编译成独立的字解码文件
            外部类名$内部类名.class
        6、内部类和外部类有重名属性时;
            调用内部类属性: this.属性名
            调用外部类属性: 外部类.this.属性名
     静态内部类:
        静态级别的内容，是在类加载时产生
        1、静态内部类访问外部类的资源
            静态: 直接引用
            非静态: 不能直接引用，外部类对象名.资源
        2、外部类访问静态内部类的资源
            静态: 内部类类名.资源
            非静态: 内部类对象名.资源名
        3、外界访问内部类资源
            静态: 外部类类名.内部类类名.资源名
            非静态: 外部类类名.内部类类名 对象名 = new 外部类类名.内部类类名();
        4、静态内部类，也会编译成独立的字解码文件
     局部内部类:
        方法执行时产生，作用范围只在方法内。
        1、局部内部类使用外部类的资源
            所在实例方法: 可以访问内部类的所有资源
            所在静态方法: 可以访问内部类的静态资源
        2、局部内部类的资源想访问，在方法内创建对象然后使用
        3、编译产生字解码文件 eg: Outer$1Inner.class
        4、如果局部内部类访问了所在方法的局部变量，则此变量会被 final修饰 (jdk 1.8之前 final需要手动显示加上)
        5、生命周期比方法长，类声明都在方法区，不是弹栈，是被 GC回收。
     匿名内部类:
        1、本质上就是局部内部类，只在当前方法内生效
        2、没有名字的类，必须要继承一个父类或实现一个接口
        3、匿名内部类其实就是一个父类的子类或一个接口的实现类
 */

/*
代码块:  一对 {}
    普通/实例代码块: (非静态，要执行得先创建)
        书写位置: 类中、方法外
        作用: 给实例变量赋值
        执行地位: 代码块优于构造器执行，多个代码块看书写顺序。
        作用域: 只能在当前代码块使用
    方法里的代码块: 按方法的代码书写顺序，不同于实例代码块。  见 MyTest的 testLocalCodeBlock()
    静态代码块:
        作用: 给静态成员做赋值、进行初始化操作
        执行地位: 代码块优于普通代码块、优于构造器执行
        无论创建多少次对象，只做一次执行 (因为是静态的)
 */

/*
类的初始化:
    目的: 类初始化就是为静态变量赋有效初始值
    过程: 底层调用<clinit>   (打断点 Debugger能看到)
            1、静态变量的显示初始化赋值 (先执行！！！)
            2、执行静态代码块
            注意: 静态方法并不算在类初始化过程！！ 方法只在执行时加载！！类的初始化与实例级别无关！！！
    初始化时机: 使用了类中的静态资源(属性或方法)、创建对象
        类的初始化 <clinit> 只会执行一次，除非手动卸载
        如果存在继承关系，会先进行父类初始化，再子类初始化
        父类与子类共用的是一个 clinit

实例初始化过程:
    目的: 实例初始化是在 new对象的过程中为实例变量赋有效初始值
    过程: 底层调用 <init>
        普通成员变量的显示初始化赋值 与 实例代码块的执行顺序看【书写顺序】！！！
    初始化时机: 创建对象
        实例初始化 <init> 创建几个对象，就会初始化几次
        如果存在继承关系，会先进行父类实例初始化，再子类实例初始化
        父类、子类 独有 <init>

混合初始化过程:
    当一个类有静态和实例，且有继承关系
    先做类初始化，先初始化父类，在初始化子类
    在做实例初始化，先初始化父类，在初始化子类     【构造方法属于实例化部分】
 */

/*
Java Bean是 Java语言在编写类时的一种标准规范
    要求:
        1、类由 public修饰，是公共的。
        2、必须具备无参构造，有参构造方法。
        3、属性必须私有化 private,并对外提供公具有共访问权限 public的get()、set()方法。
 */

/*
方法重载 要求: 1、方法名称相同：要进行方法重载，所有重载的方法必须具有相同的名称。
            2、参数列表不同： 参数数量不同：重载的方法可以有不同数量的参数。 参数类型不同：重载的方法可以有不同类型的参数。 参数顺序不同：重载的方法可以有相同数量和类型但不同顺序的参数。
            3、返回类型可以相同也可以不同：方法的返回类型对于重载并不重要，即使返回类型不同也无法单独作为重载的依据。
方法重写 要求:
            1、参数列表与被重写方法的参数列表必须完全相同。
            2、返回类型与被重写方法的返回类型可以不相同，但是必须是父类返回值的派生类（java5 及更早版本返回类型要一样，java7 及更高版本可以不同）。
            3、访问权限不能比父类中被重写的方法的访问权限更低。 例如：如果父类的一个方法被声明为 public，那么在子类中重写该方法就不能声明为 protected。
            4、父类的成员方法只能被它的子类重写。
            5、声明为 final 的方法不能被重写。
            6、声明为 static 的方法不能被重写，但是能够被再次声明。
            7、子类和父类在同一个包中，那么子类可以重写父类所有方法，除了声明为 private 和 final 的方法。
            8、子类和父类不在同一个包中，那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
 */

/*
可变长参数的使用: sum(int... arr)
    语法: 数据类型... 变量名
    注意事项： 1、可变长参数底层使用的是数组
             2、形参的个数不固定，可以传递 0 ~ n个实参，都会存在数数组中
             3、可以传递数组，共享一个地址 sum(new int[n])
             4、可变长参数必须在形参列表的最后一位
             5、一个方法的可变长参数只能有一个
*/

/*
ENUM 枚举类型:
    jdk 1.5之前: 详见 tryEnum/enumBefore
    jdk 1.5及之后: 详见 tryEnum/enumNow
        提供关键字 enum
        默认构造器私有
        创建自身类型的对象过程省略
        默认枚举会转换成 class, extends Enum
        switch -> byte short int char string 枚举
 */

/*
Exception 异常:
    程序在运行时，和预期的执行流程不同，出现了问题，导致程序不能继续执行。
    java.lang.Throwable【异常也是对象】
        - java.lang.Error 【错误】 (系统问题、JVM问题。程序员无法干涉的，只能避免、规避)
        - java.lang.Exception 【异常】 (可以处理的异常)
            - 编译时异常: 编译时报错
            - 运行时异常: 运行时可能出错
    异常的抛出:
        自动抛出: 详见 AutoThrowTest
        手动抛出: 详见 HandleThrowTest
    异常的处理:
        throws 懒处理: A有异常，告知调用者，A可能会出现异常，调用者记得解决。
        try-catch 积极处理: A有异常，解决！告知调用者，程序是正常执行的。
    异常下的方法重写:
 */
/*
String: 概念   构造器、成员方法见 StringTest
    1、java.lang.String  不用导包
    2、final class String 不能被继承
    3、支持直接赋值、new 创建对象
    4、private final byte[] value;     byte[] value = {97, 98, 99};
            jdk8   final char[] value;
            jdk9   final byte[] value;
    5、相同字符串可以复用
    6、字符串是存储在字符串常量池。   (堆)

    【字符串常量】 + 【字符串常量】  还是常量池里的地址值
    【字符串变量】 + 【字符串常量】 -> new StringBuilder   可以 var.intern(); 拿到对应常量池中的字符串地址
    【字符串变量】 + 【字符串变量】 -> new StringBuilder

String 使用时的一些细节:
    虽然 String是引用数据类型，在调用方法传递时，传递的是地址
    然而 String是用 final修饰的，地址不可变。
    当另一个方法进行字符串的更改时，本质上是新建。
    s = "a"+"b"+"c"+"d"   -> javac -> s = "abcd" 所以堆中只新增一个值

String 是不可变字符串
StringBuilder 是可变字符串，不是线程安全的
StringBuffer 是可变字符串，是线程安全的

包装类:
    装箱: 将基本数据类型转换为包装类型
         可以直接将基本类型的值，赋给包装类型
            手动装箱: 包装类名.valueOf(基本类型) 【 Integer.valueOf();】
            自动装箱: 包装类 对象名 = 基本类型值;
    拆箱: 将包装类型转换为基本类型
         可以将包装类型的值，赋给基本类型 【 int.intValue();】
            手动拆箱: 包装对象名.xxxValue()    xxx 为要转换的基本数据类型
            自动拆箱: 直接将包装类型的对象赋值给基本类型   会自动调用 xxxValue
    【 包装类底层是 final修饰的，固定不可变.  传递改值 -> new 包装类() 】
    【 Double d = 200; --> Double d = Integer.valueOf(200) 会报错】
    【 整数类型缓冲区，提前创建好了的 Integer对象 [-128, 127]、字符型也有 [0, 127]】
  字符串转 int
    Integer.parseInt() 将字符串类型转换为 【基本类型】
    Integer.valueOf() 将字符串类型转换为 【包装类型】

   10进制转 2进制   Integer.toBinaryString(int value);
   10进制转 8进制   Integer.toOctalString(int value);
   10进制转 16进制   Integer.toHexString(int value);
 */

/*
补充 Vector(已弃用): Vector<Integer> vector = new Vector<>();
    创建 vector对象时，初始的数组容量是 10
    vector数组已满，扩容是原来空间的 2倍
    可以构造是指定 initCapacity 、 capacityIncrement
        若指定增量 capacityIncrement则扩容 capacityIncrements这么多，即新容量为: oldCapacity + increments;
与 ArrayList的区别:
    数组开辟空间不同: arraylist: 0  vector: 10
    开辟空间时机不同: arraylist: 第一次添加数据时 vector: 创建对象时
    扩容规则不同: arraylist: 1.5倍 vector: 2倍 (default)
    线程安全: arraylist: 线程不安全 vector: 线程安全

deepseek: 适合顺序存储场景，避免大块连续内存申请
    1.5倍扩容保证 add()操作的均摊时间复杂度为 O(1)
  等比数列求和公式证明：S = n + n/1.5 + n/(1.5)^2 +... ≈ 3n
 */
/*
hashMap:
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    static final int TREEIFY_THRESHOLD = 8;
    static final int UNTREEIFY_THRESHOLD = 6;
    static final int MIN_TREEIFY_CAPACITY = 64;

deepseek:
    那为什么HashMap要扩容两倍呢？可能跟哈希函数有关。当数组扩容时，所有的元素需要重新计算哈希并分配到新的桶里。如果容量是2的幂，那么计算索引时可以用位运算代替取模，这样更高效

特性	          HashMap	              ArrayList
扩容倍数	        2倍	                    1.5倍
核心考量	     哈希分布均匀性	        内存空间利用率
数学原理	     模运算优化(2^n)	        等比数列求和优化
时间复杂度   O(n)但分摊到操作次数少	  O(n)但每次复制量更小
适用场景	   随机访问为主的哈希结构	   顺序访问的动态数组
 */

/*
1、List、Set、Map体系的集合特点是什么?
    List: 有序、不唯一
    Set: 无序、唯一
    Map: 键值对   key 无序、唯一
                value 无序、不唯一

2、ArrayList 和 LinkedList实现的原理是什么？特点是什么？
    ArrayList 底层是数组，特点是可以按下标查询，速度快，但非末尾的元素添加效率低
    LinkedList 底层是双向链表，特点是添加、删除操作快

3、HashSet、LinkedHashSet的实现原理是什么？存储的数据有什么要求？
    hashSet 数组 + 链表/红黑树
    LinkedHashSet 数组 + 双向链表
    为保证数据去重，存储的数据必须要重写hashCode方法 和 equals方法

4、TreeSet在存储元素时，是通过什么方式实现去重的？
    内部实现 Comparable接口 重写 compareTo()方法 或者 外部传入 comparator比较器 重写compare()方法

5、HashMap 和 Hashtable的区别是什么？
    hashMap:
        线程不安全、效率高
        允许null作为key和value，作为key只允许一个
        初始空间为 16
    hashTable:
        线程安全，效率低
        不允许null作为key和value
        初始空间为 11
 */
/*
泛型的上线:
    一般体现在方法上:
        T extends 类型
        T extends Comparable 传入的未知类型必须是 implements Comparable
        T extends 类型 1 & 类型 2
        T extends Number & Comparable 传入的类型必须是 Number的子类，Comparable的实现类
            父类在前，继承在后。

泛型的擦除:
    1、需要类型，没有传类型，会将泛型擦除掉，采用类的最顶级
    2、泛型只存在于编译期，运行时泛型就没有了
    3、泛型没有多态，前后泛型必须是一致的

泛型的通配符: ?
    T: 未知类型，传入的时候类型确定了    【占位符】
    ?: 知道要传一个类型，但不关注传入的类型是什么   【通配符】
    ? super T 通配符的下限为 T 比如: T 如果是 dog那可以传 Animal,这是在某些情况下，比如比较父类有的属性 【泛型没有多态】
 */
/*
(传统的)线程的生命周期:
    新建 new 创建了线程对象，但是没有启动
    就绪 runnable 调用 start后，不是立刻执行，而是等待 cpu分配资源(调度)
    运行 running 线程指定代码，没执行完毕会回到就绪状态
    阻塞 blocked join、sleep、wait、scanner、synchronized(){}
    死亡 dead 强制停止、异常停止、run方法正确执行完毕

JDK的线程 6种状态:
    NEW: 创建了线程对象，但是没有启动
    RUNNABLE: 可运行状态(就绪和运行)
    BLOCKED: 等待线程的锁
    WAITING: 死等 join() wait() Scanner
    TIMED_WAITING: 有限期等待 join(millis) wait(millis) sleep(millis)
    TERMINATED: 死亡状态 强制停止、异常停止、run方法正确执行完毕
 */
/*
锁的释放与死锁:
    锁的释放:
        1、当线程的任务同步代码块、同步方法执行完毕了，会自动释放锁
        2、线程的任务同步代码块、同步方法出现异常了，导致线程因异常而终止，会自动释放锁
        3、调用了wait()，会自动释放锁，进入阻塞状态，等待唤醒 notify
    如果出现死锁，优先让其中某一个线程拿到资源执行掉
 */
/*
sleep()和 wait()的区别:
    1、sleep()必须指定时间，是有限期等待。wait()可指定、可不指定时间，可能是有限期，也可能是无限期等待。
    2、sleep()是时间到达，自动醒来，进入就绪状态。wait()如果没有指定时间，需要 notify()进行唤醒
    3、sleep()是 Thread类的静态方法。wait()是 Object类的实例方法
    4、sleep()用 Thread类调用，wait()用 锁对象调用 【锁对象可以是任意引用类型的对象】
    5、sleep() 不释放锁，wait()释放锁
 */
/*
file类:
    概念: file类是资源中的文件或文件夹的抽象表示，只是用于描述本地磁盘的文件或文件夹，
        但是不代表这个文件，本身不能对文件的内容进行操作，想操作内容用 IO流
IO流:
    概念: 从一个设备上将数据传输给另一个设备，掌握IO是为了传输数据，将内存中的数据存储到硬盘上。 【以字节为单位】
        原来学的存储数据的方式: 数组、集合，都属于内存数据，而内存的特点是: 临时存储数据。
    分类: (输入流、输出流)
        字节流: 【万能流】计算机中所有的数据都是以字节为单位。
        字符流: 专注于读取字符数据的。
 */
/*
java语言的反射机制:
    java反射机制是在【运行状态】中，
    对于任意一个类，都能够知道这个类的所有属性和方法；
    对于任意一个对象，都能够调用它的任意一个方法和属性；
    这种动态获取的信息以及动态调用对象的方法的功能,Class对象是反射的根源。
 */
/*
- 注释: 解释一行代码或一段代码。      给程序员看的。           单行、多行、文档
- 注解: 对代码做解释，           给程序看的，        程序在运行时，根据注解，执行对应的操作。
    注解的作用:
        1、安全检测  是否符合要求
        2、生成 API文档   tool -> generate JavaDoc...
        3、替代配置文件 【重点】
 */
}
